package com.server;

import java.io.DataOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.TimeUnit;

import com.shared.QueueElement;
import com.shared.SystemMsg;

/**
 * This is a queue handler to deal with the elements in the queue.
 * 
 * @author jiafanz
 *
 */
public class QueueHandler extends Thread
{
    private Queue<QueueElement> queue;
    private Map<QueueElement, QueueElement> pairMap = new HashMap<QueueElement, QueueElement>();
    
    /**
     * Constructor of the QueueHandler.
     * @param queue the queue being constructed.
     */
    public QueueHandler(Queue<QueueElement> queue)
    {
        this.queue = queue;
        
        // start the thread.
        start();
    }
    
    /**
     * {@inheritDoc}
     */
    public void run()
    {
        while (true)
        {
            synchronized (queue)
            {
                // the thread constantly checks if there are any elements
                // being pushed into the queue.
                if (queue.size() > 1)
                {
                    QueueElement ele1 = queue.poll();
                    QueueElement ele2 = queue.poll();
                    
                    pairMap.put(ele1, ele2);
                    pairMap.put(ele2, ele1);
                    
                    new ServerThread(this, ele1);
                    new ServerThread(this, ele2);
                    
                    sendMsgToQueueElement(ele1, SystemMsg.Serviced.toString());
                    sendMsgToQueueElement(ele2, SystemMsg.Serviced.toString());
                }
                else
                {
                    // take a snap for 2 seconds
                    // JIAFANZ: consider shorting the delay of this timer.
                    try
                    {
                        TimeUnit.SECONDS.sleep(2);
                    } 
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    /**
     * Checks whether or not a pair element is available in the queue.
     * @return
     */
    public boolean isPairAvailable()
    {
        return queue.size() > 1;
    }
    
    /**
     * Send a message to the defined QueueElement.
     * @param element the queue element to send message to.
     * @param message the message being sent.
     */
    public void sendMsgToQueueElement(QueueElement element, String message)
    {
        DataOutputStream dout = element.getOutputStream();
        
        // ... and send the message
        try
        {
            dout.writeUTF(message);                    
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
    }
    
    /**
     * Send a message to the defined QueueElement's pair element..
     * @param element the queue element to send message to.
     * @param message the message being sent.
     */
    public void sendMsgToPairElement(QueueElement element, String message)
    {
        if (pairMap.containsKey(element))
        {
            QueueElement pair = pairMap.get(element);
            
            DataOutputStream dout = pair.getOutputStream();
            
            // ... and send the message
            try
            {
                dout.writeUTF(message);                    
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
        }
        else
        {
            // this element does not exist
            // JIAFANZ: deal with this...
        }
    }
    
    /**
     * Remove the connection for a particular queue element.
     * @param element the element to remove the connection from.
     */
    public void removeConnection(QueueElement element)
    {
        QueueElement pairElement = pairMap.get(element);
        pairMap.remove(pairElement);
        pairMap.remove(element);
        
        // make sure it is closed
        try
        {
            if (element != null && !element.getSocket().isClosed())
            {
                sendMsgToQueueElement(element, SystemMsg.Disconnected.toString());                
                element.getSocket().close();
            }
            
            if (pairElement!= null && !pairElement.getSocket().isClosed())
            {
                sendMsgToQueueElement(pairElement, SystemMsg.Disconnected.toString());                
                pairElement.getSocket().close();
            }
        }
        catch (Exception ex)
        {
            System.out.println("Error closing: " + element.getSocket());
            ex.fillInStackTrace();
        }

    }
}
